/*
 * Code for blog.techhead.biz
 * Distributed under BSD-style license
 */
package biz.techhead.funcy;

import biz.techhead.funcy.ref.LazyReference;
import biz.techhead.funcy.ref.Reference;
import biz.techhead.funcy.ref.References;
import static biz.techhead.funcy.Iterables.immutableList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * This class does one thing, cache values for a class of Enum.
 * Should it become more valuable in the future, I will make it public.
 *
 * @author Jonathan Hawkes <jhawkes at techhead.biz>
 */
class Enums {
    private Enums() {}

    @SuppressWarnings("unchecked")
    public static <E extends Enum<E>> List<E> values(Class<E> enumClass) {
        Reference<List<E>> v = ( Reference<List<E>> ) cache.get(enumClass);
        if (v == null) {
            // a variation on double-checked locking
            synchronized (cache) {
                v = ( Reference<List<E>> ) cache.get(enumClass);
                if (v == null) {
                    v = new SoftEnumValuesReference(enumClass);
                    cache.put(enumClass, v);
                }
            }
        }
        return ( List<E> ) v.get();
    }

    private static <E extends Enum<E>>
            List<E> collectValues(Class<E> enumClass) {
        /*
        Set<E> set = EnumSet.allOf(enumClass);
        E[] array = set.toArray(
                ( E[] ) Array.newInstance(enumClass, set.size()) );
        return Collections.unmodifiableList( Arrays.asList(array) );
        */ 
        return immutableList( EnumSet.allOf(enumClass) );
    }

    private static abstract class LazySoftReference<T>
            extends LazyReference<T> {

        LazySoftReference() {
            super( References.<T>soft() );
        }
    }

    private static class SoftEnumValuesReference<E extends Enum<E>>
            extends LazySoftReference<List<E>> {

        private final Class<E> enumClass;

        public SoftEnumValuesReference(Class<E> enumClass) {
            this.enumClass = enumClass;
        }

        @Override
        protected List<E> initValue() {
            return collectValues(enumClass);
        }
    }

    @SuppressWarnings("unchecked") // mixed key and value types
    private static final ConcurrentMap cache = new ConcurrentHashMap();
}
